/*****************************************************************************
*	Copyright (C)2009 Ali Corporation. All Rights Reserved.
*
*	File:    i2c_gpio.S
*
*	Description:   GPIO type of i2c driver for Panel chip CH455K. 
*
*	History:
*	Date				Athor		Version		Reason
*	=======================================================
*     1. 04.07.2009		Mao Feng		Ver 0.1		Create file.
*     
******************************************************************************/
#include <sys_config.h>
#include <hal/mips.h>
#include <hal/machine.h>

##Options
#define I2C_GPIO_SCL		SYS_I2C_SCL	// 97	// 75
#define I2C_GPIO_SDA		SYS_I2C_SDA	// 106	// 76

#define _HALF_PERIOD_COUNT	200

##bps(40K)-->half period(12.5us)
##crystal(27M)-->1s count(27M/4)-->half period count(27*12.5/4=85)-->CPU clk(170)
##crystal(28.8M)-->1s count(28.8M/32)-->half period count(28.8*12.5/32=12)-->CPU clk(24)
##crystal(13.5M)-->1s count(13.5M/32)-->half period count(13.5*12.5/32=6)-->CPU clk(12)

#define I2C_GPIO_READ_TIMEOUT	7


#define CONFIGDATA_BASE	0xAFC00000
#define CRYSTAL_TYPE		(CONFIGDATA_BASE+0x9E)	# 8bit. 0:27M; 1:13.5M

##GPIO registers
#if (SYS_GPIO_MODULE == M6303GPIO)
#define HAL_GPIO_DIR_REG	0xb8000058
#define HAL_GPIO_DI_REG		0xb8000050
#define HAL_GPIO_DO_REG		0xb8000054

#define HAL_GPIO1_DIR_REG	0xb80000b8
#define HAL_GPIO1_DI_REG	0xb80000b0
#define HAL_GPIO1_DO_REG	0xb80000b4

#define HAL_GPIO2_DIR_REG	0xb80000d4
#define HAL_GPIO2_DI_REG	0xb80000cc
#define HAL_GPIO2_DO_REG	0xb80000d0

#define HAL_GPIO3_DIR_REG	0xb8000000
#define HAL_GPIO3_DI_REG	0xb8000000
#define HAL_GPIO3_DO_REG	0xb8000000

#define HAL_GPIO4_DIR_REG	0xb8000000
#define HAL_GPIO4_DI_REG	0xb8000000
#define HAL_GPIO4_DO_REG	0xb8000000
#elif (SYS_GPIO_MODULE == M3602F_GPIO)
#define HAL_GPIO_DIR_REG	0xb8000058
#define HAL_GPIO_DI_REG		0xb8000050
#define HAL_GPIO_DO_REG		0xb8000054

#define HAL_GPIO1_DIR_REG	0xb80000d8
#define HAL_GPIO1_DI_REG	0xb80000d0
#define HAL_GPIO1_DO_REG	0xb80000d4

#define HAL_GPIO2_DIR_REG	0xb80000f8
#define HAL_GPIO2_DI_REG	0xb80000f0
#define HAL_GPIO2_DO_REG	0xb80000f4

#define HAL_GPIO3_DIR_REG	0xb8000358
#define HAL_GPIO3_DI_REG	0xb8000350
#define HAL_GPIO3_DO_REG	0xb8000354

#define HAL_GPIO4_DIR_REG	0xb8000458
#define HAL_GPIO4_DI_REG	0xb8000450
#define HAL_GPIO4_DO_REG	0xb8000454
#endif

# MACRO: _i2c_gpio_half_period()
# Description:	delay half period.
	.macro	_i2c_gpio_half_period
	li		t0, _HALF_PERIOD_COUNT
88:
	bnez		t0, 88b
	addiu	t0, -1
	.endm


/***************************************************************
*Function Name:	_i2c_gpio_bit_set
*Arguments: 	a0: gpio num
*			a1: 0->low, 1->high
*			v0: 0->success, -1->fail
*Description:	Set a bit while gpio dir's out.
*Notice:		Useing t0-t2, v0, a0, a1, no sub-functions.
***************************************************************/
	.global	_i2c_gpio_bit_set
	.ent		_i2c_gpio_bit_set
	.set		noreorder
_i2c_gpio_bit_set:
	li		t0, 32
	bge		a0, t0, 64f
	nop
32:
	li		t0, HAL_GPIO_DO_REG
	lw		t1, (t0)
	b		1f
	nop
	
64:
	li		t0, 64
	bge		a0, t0, 96f
	nop

	li		t0, HAL_GPIO1_DO_REG
	lw		t1, (t0)
	b		1f
	nop
	
96:
	li		t0, 96
	bge		a0, t0, 128f
	nop
	
	li		t0, HAL_GPIO2_DO_REG
	lw		t1, (t0)
	b		1f
	nop

128:
	li		t0, 128
	bge		a0, t0, 144f
	nop
	li		t0, HAL_GPIO3_DO_REG
	lw		t1, (t0)
	b		1f
	nop	

144:
	li		t0, 144
	bge		a0, t0, 160f
	nop
	li		t0, HAL_GPIO4_DO_REG
	lw		t1, (t0)
	
1:
	li		t2, 0x1
	sllv		t2, t2, a0					##shift left logically by variable

	bnez		a1, 2f
	nop
	
	not		t2, t2					##set 0
	and		t1, t2
	sw		t1, (t0)	
	b		3f
	nop
	
2:									##set 1
	or		t1, t2
	sw		t1, (t0)
	
3:									
	jr		ra
	li		v0, 0
	
160:
	jr		ra
	li		v0, -1

	.set reorder
	.end _i2c_gpio_bit_set


/***************************************************************
*Function Name:	_i2c_gpio_bit_get
*Arguments: 	a0: gpio num
*			v0: 0->low, 1->high, -1->fail
*Description:	get a bit while gpio dir's in.
*Notice:		useing t0-t1, v0, a0, no sub-functions.
***************************************************************/
	.global	_i2c_gpio_bit_get
	.ent		_i2c_gpio_bit_get
	.set		noreorder
_i2c_gpio_bit_get:
	li		t0, 32
	bge		a0, t0, 64f
	nop
32:	
	li		t0, HAL_GPIO_DI_REG
	lw		t1, (t0)
	b		1f
	nop
	
64:
	li		t0, 64
	bge		a0, t0, 96f
	nop

	li		t0, HAL_GPIO1_DI_REG
	lw		t1, (t0)
	b		1f
	nop
	
96:
	li		t0, 96
	bge		a0, t0, 128f
	nop
	li		t0, HAL_GPIO2_DI_REG
	lw		t1, (t0)
	b		1f
	nop
128:
	li		t0, 128
	bge		a0, t0, 144f
	nop
	li		t0, HAL_GPIO3_DI_REG
	lw		t1, (t0)
	b		1f
	nop

144:
	li		t0, 144
	bge		a0, t0, 160f
	nop
	li		t0, HAL_GPIO4_DI_REG
	lw		t1, (t0)	
	
1:
	srlv		t1, t1, a0
	and		v0, t1, 0x01
	jr		ra
	nop
	
160:
	jr		ra
	li		v0, -1

	.set reorder
	.end _i2c_gpio_bit_get


/***************************************************************
*Function Name:	_i2c_gpio_dir_set
*Arguments: 	a0: gpio num
*			a1: 0->in, 1->out
*			v0: 0->success, -1->fail			
*Description:	set GPIO direction.
*Notice:		useing t0-t2, v0, a0, a1, no sub-functions.
***************************************************************/
	.global	_i2c_gpio_dir_set
	.ent		_i2c_gpio_dir_set
	.set		noreorder
_i2c_gpio_dir_set:
	li		t0, 32
	bge		a0, t0, 64f
	nop
32:	
	li		t0, HAL_GPIO_DIR_REG
	lw		t1, (t0)
	b		1f
	nop

64:
	li		t0, 64
	bge		a0, t0, 96f
	nop
	
	li		t0, HAL_GPIO1_DIR_REG
	lw		t1, (t0)
	b		1f
	nop

96:
	li		t0, 96
	bge		a0, t0, 128f
	nop

	li		t0, HAL_GPIO2_DIR_REG
	lw		t1, (t0)
	b		1f
	nop
128:
	li		t0, 128
	bge		a0, t0, 144f
	nop

	li		t0, HAL_GPIO3_DIR_REG
	lw		t1, (t0)
	b		1f
	nop

144:
	li		t0, 144
	bge		a0, t0, 160f
	nop

	li		t0, HAL_GPIO4_DIR_REG
	lw		t1, (t0)

1:
	li		t2, 0x1
	sllv		t2, t2, a0					##shift left logically by variable

	bnez		a1, 2f
	nop
	
	not		t2, t2					##set 0
	and		t1, t2
	sw		t1, (t0)
	b		3f
	nop
	
2:									##set 1
	or		t1, t2
	sw		t1, (t0)
	
3:									
	jr		ra
	li		v0, 0

160:
	jr		ra
	li		v0, -1

	.set reorder
	.end _i2c_gpio_dir_set


/***************************************************************
*Function Name:	_i2c_gpio_dir_get
*Arguments: 	a0: gpio num
*			v0: 0->low, 1->high, -1->fail
*Description:	get GPIO direction.
*Notice:		useing t0-t1, v0, a0, no sub-functions.
***************************************************************/
	.global	_i2c_gpio_dir_get
	.ent		_i2c_gpio_dir_get
	.set		noreorder
_i2c_gpio_dir_get:
	li		t0, 32
	bge		a0, t0, 64f
	nop
32:	
	li		t0, HAL_GPIO_DIR_REG
	lw		t1, (t0)
	b		1f
	nop
	
64:
	li		t0, 64
	bge		a0, t0, 96f
	nop

	li		t0, HAL_GPIO1_DIR_REG
	lw		t1, (t0)
	b		1f
	nop
	
96:
	li		t0, 96
	bge		a0, t0, 128f
	nop
	
	li		t0, HAL_GPIO2_DIR_REG
	lw		t1, (t0)
	b		1f
	nop
	
128:
	li		t0, 128
	bge		a0, t0, 144f
	nop
	
	li		t0, HAL_GPIO3_DIR_REG
	lw		t1, (t0)
	b		1f
	nop

144:
	li		t0, 144
	bge		a0, t0, 160f
	nop

	li		t0, HAL_GPIO4_DIR_REG
	lw		t1, (t0)
1:
	srlv		t1, t1, a0
	and		v0, t1, 0x01
	jr		ra
	nop
	
160:
	jr		ra
	li		v0, -1

	.set reorder
	.end _i2c_gpio_dir_get


/***************************************************************
*Function Name:	_i2c_gpio_start_phase
*Arguments:  	v0: 0->success, -1->fail
*
*Description:	Generate Start Condition
*Stream Format:
*			SCL	____/------\_
*			SDA	_/------\____
*Notice:		Store ra in t3, so not use t3 in sub-functions.
***************************************************************/
	.global	_i2c_gpio_start_phase
	.ent		_i2c_gpio_start_phase
	.set		noreorder
_i2c_gpio_start_phase:
	move	t3, ra					##store ra in t3
	
	li		a1, 1					##set SDA output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SDA
	
	li		a1, 1					##set SCL output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SCL

	li		a1, 1					##set SDA high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SDA

	jal		_i2c_gpio_bit_get			##get SCL
	li		a0, I2C_GPIO_SCL			
	bnez		v0, 1f
	nop

	_i2c_gpio_half_period				##delay half period
	
1:
	li		a1, 1					##set SCL high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL

	_i2c_gpio_half_period				##delay half period

	jal		_i2c_gpio_bit_get			##get SCL
	li		a0, I2C_GPIO_SCL			
	beqz		v0, 2f
	nop	

	jal		_i2c_gpio_bit_get			##get SDA
	li		a0, I2C_GPIO_SDA			
	beqz		v0, 2f
	nop	

	li		a1, 0					##set SDA low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SDA

	_i2c_gpio_half_period				##delay half period

	li		a1, 0					##set SCL low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL

	move	ra, t3					##fetch ra from t3
	jr		ra
	li		v0, 0					##success

2:	
	move	ra, t3					##fetch ra from t3
	jr		ra
	li		v0, -1					##fail
	
	.set reorder
	.end _i2c_gpio_start_phase


/***************************************************************
*Function Name:	_i2c_gpio_stop_phase
*Arguments:	v0: 0->success, -1->fail
*
*Description:	Generate Stop Condition
*Stream Format:
*			SCL	____/-------
*			SDA	_______/----
*Notice:		Store ra in t3, so not use t3 in sub-functions.
***************************************************************/
	.global	_i2c_gpio_stop_phase
	.ent		_i2c_gpio_stop_phase
	.set		noreorder
_i2c_gpio_stop_phase:
	move	t3, ra					##store ra in t3.
	
	li		a1, 1					##set SDA output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SDA
	
	li		a1, 1					##set SCL output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SCL
	
	li		a1, 0					##set SDA low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SDA
	
	_i2c_gpio_half_period				##delay half period

	li		a1, 1					##set SCL high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL

	_i2c_gpio_half_period				##delay half period

	jal		_i2c_gpio_bit_get			##get SCL
	li		a0, I2C_GPIO_SCL			
	beqz		v0, 1f
	nop	

	li		a1, 1					##set SDA high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SDA
									##may need to delay here.
									
	jal		_i2c_gpio_bit_get			##get SDA
	li		a0, I2C_GPIO_SDA			
	beqz		v0, 1f
	nop	

	move	ra, t3					##fetch ra from t3
	jr		ra
	li		v0, 0					##success

1:	
	move	ra, t3					##fetch ra from t3
	jr		ra
	li		v0, -1					##fail
	
	.set		reorder
	.end		_i2c_gpio_stop_phase


/***************************************************************
*Function Name:	_i2c_gpio_set_byte
*Arguments: 
*			a2:	data
*			v0:	ack from slave(0->success, 1->fail)
*Description:	send a byte to i2c bus.
*			Using t3, t5, t7 as global variables:
*				t3->store ret addr			
*				t5->store shifted data
*				t7->store shifting times(8 times)
***************************************************************/
	.global	_i2c_gpio_set_byte
	.ent		_i2c_gpio_set_byte
	.set		noreorder
_i2c_gpio_set_byte:
	move	t3, ra

##Send a byte bit after bit.	
	move	t5, a2
	
	li		t7, 7						##bit7-0
1:
	li		a1, 1					##set SDA output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SDA
	
	li		a1, 1					##set SCL output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SCL

	li		a1, 0					##set SCL low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	
	
	andi		t2, t5, 0x80
	srl		t2, 7
	bnez		t2, 2f
	nop

	b		3f
	li		a1, 0 					##set 0
2:	
	li		a1, 1					##set 1
3:	
	jal		_i2c_gpio_bit_set
	li		a0, I2C_GPIO_SDA

	_i2c_gpio_half_period				##delay half period

	li		a1, 1					##set SCL high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	_i2c_gpio_half_period				##delay half period

	li		a1, 0					##set SCL low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	sll		t5, 1
	bnez		t7, 1b
	sub		t7, 1

##Get ack.
	li		a1, 0					##set SDA input
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SDA
	
	li		a1, 1					##set SDA high
	jal		_i2c_gpio_bit_set
	li		a0, I2C_GPIO_SDA	
	
	_i2c_gpio_half_period				##delay half period

	li		a1, 1					##set SCL high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	_i2c_gpio_half_period				##delay half period

	jal		_i2c_gpio_bit_get			##get SDA
	li		a0, I2C_GPIO_SDA

	move 	t7, v0
	
	li		a1, 0					##set SCL low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	move	v0,	t7
	move	ra,	t3
	jr		ra
	nop
	
	.set		reorder
	.end		_i2c_gpio_set_byte

	
/***************************************************************
*Function Name:	_i2c_gpio_get_byte
*Arguments: 
*			v0:	data
*Description:	receive a byte to i2c bus.
*			Using t3, t5, t7 as global variables:
*				t3->store ret addr			
*				t5->store shifted data
*				t7->store shifting times(8 times)
***************************************************************/
	.global	_i2c_gpio_get_byte
	.ent		_i2c_gpio_get_byte
	.set		noreorder
_i2c_gpio_get_byte:
	move	t3, ra

	li		a1, 0					##set SDA input
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SDA

	li		a1, 1					##set SCL output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SCL
	
##get data
	li		t7, 7						##bit7-0
1:
	li		a1, 1					##set SDA high
	jal		_i2c_gpio_bit_set
	li		a0, I2C_GPIO_SDA	
	
	_i2c_gpio_half_period				##delay half period

	li		a1, 1					##set SCL high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	_i2c_gpio_half_period				##delay half period

	jal		_i2c_gpio_bit_get			##get SDA
	li		a0, I2C_GPIO_SDA

	or	 	t5, v0

	li		a1, 0					##set SCL low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	sll		t5, 1
	bnez		t7, 1b
	sub		t7, 1	
	srl		t5, 1						##shift left 8 times right 1 time
	
##send ack
	li		a1, 1					##set SDA output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SDA
	
	li		a1, 0					##set SCL low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL		

	li		a1, 0 					##ack: set SDA low
	jal		_i2c_gpio_bit_set
	li		a0, I2C_GPIO_SDA

	_i2c_gpio_half_period				##delay half period

	li		a1, 1					##set SCL high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	_i2c_gpio_half_period				##delay half period

	li		a1, 0					##set SCL low
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL	

	move	v0, t5
	move	ra, t3
	jr		ra
	nop

	.set		reorder
	.end		_i2c_gpio_get_byte


/***************************************************************
*Function Name:	i2c_gpio_init_s
*Arguments: 	
*Description:	
*Notice:		Store ra in t3, so not use t3 in sub-functions.
***************************************************************/
	.global	i2c_gpio_init_s
	.ent		i2c_gpio_init_s
	.set		noreorder
i2c_gpio_init_s:	
	move	t3, ra					##store ra in t3.

	li		a1, 1					##set SDA high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SDA

	li		a1, 1					##set SCL high
	jal		_i2c_gpio_bit_set			
	li		a0, I2C_GPIO_SCL

	li		a1, 1					##set SCL output
	jal		_i2c_gpio_dir_set			
	li		a0, I2C_GPIO_SCL
	
	move	ra, t3					##fetch ra from t3
	jr		ra
	nop
	
	.set		reorder
	.end		i2c_gpio_init_s


/***************************************************************
*Function Name:	i2c_gpio_read_s
*Arguments: 	a2:	slave address
*			v0:	data 
*Description:	read a byte per time
*			Store ra in t4, retry count in t6, So not use t4 and t6 in this func or its sub-funcs.
*			Should not use t3, t5, t7 as global variables in this function.
***************************************************************/
	.global	i2c_gpio_read_s
	.ent		i2c_gpio_read_s
	.set		noreorder
i2c_gpio_read_s:
	move	t4, ra
	li		t6, I2C_GPIO_READ_TIMEOUT
1:
	jal		_i2c_gpio_start_phase		##START
	nop
	
	jal		_i2c_gpio_set_byte		##set addr in a2
	nop

	beqz		v0, 2f
	nop

	jal		_i2c_gpio_stop_phase		##STOP
	nop

	_i2c_gpio_half_period				##delay half period

	bnez		t6, 1b
	addiu	t6, -1

2:
	blez		t6, 3f
	nop

	jal		_i2c_gpio_get_byte		##get byte
	nop

	move	t7, v0
	jal		_i2c_gpio_stop_phase		##STOP
	nop
	move	v0, t7

	b		4f
	nop
	
3:
	li		v0, -1
	
4:
	move	ra, t4
	jr		ra
	nop
	
	.set		reorder
	.end		i2c_gpio_read_s


/***************************************************************
*Function Name:	i2c_gpio_write_s
*Arguments: 	a2:	slave address
*			a3:	data	 
*Description:	read a byte per time
*			Store ra in t4, retry count in t6, So not use t4 and t6 in this func or its sub-funcs.
*			Should not use t3, t5, t7 as global variables in this function.
***************************************************************/
	.global	i2c_gpio_write_s
	.ent		i2c_gpio_write_s
	.set		noreorder
i2c_gpio_write_s:	
	move	t4, ra
	li		t6, I2C_GPIO_READ_TIMEOUT
1:
	jal		_i2c_gpio_start_phase		##START
	nop
	
	jal		_i2c_gpio_set_byte		##set addr in a2
	nop

	beqz		v0, 2f					##check ack
	nop

	jal		_i2c_gpio_stop_phase		##STOP
	nop

	_i2c_gpio_half_period				##delay half period

	bnez		t6, 1b
	addiu	t6, -1

2:
	blez		t6, 3f
	nop

	jal		_i2c_gpio_set_byte		##set addr in a3
	move	a2, a3

	jal		_i2c_gpio_stop_phase		##STOP
	nop

	b		4f
	li		v0, 0
	
3:
	li		v0, -1
	
4:
	move	ra, t4
	jr		ra
	nop
	
	.set		reorder
	.end		i2c_gpio_write_s
